// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <type_traits>
#include <utility>

#include "base/profiler/native_unwinder_android.h"
#include "chrome/android/features/stack_unwinder/public/function_types.h"
#include "chrome/android/features/stack_unwinder/public/memory_regions_map.h"
#include "chrome/android/modules/stack_unwinder/internal/jni_headers/StackUnwinderModuleContentsImpl_jni.h"

namespace {

class MemoryRegionsMap : public stack_unwinder::MemoryRegionsMap {
 public:
  MemoryRegionsMap(std::unique_ptr<unwindstack::Maps> maps,
                   std::unique_ptr<unwindstack::Memory> memory)
      : maps_(std::move(maps)), memory_(std::move(memory)) {}

  unwindstack::Maps* maps() { return maps_.get(); }
  unwindstack::Memory* memory() { return memory_.get(); }

 private:
  std::unique_ptr<unwindstack::Maps> maps_;
  std::unique_ptr<unwindstack::Memory> memory_;
};

}  // namespace

std::unique_ptr<stack_unwinder::MemoryRegionsMap> CreateMemoryRegionsMap() {
  return std::make_unique<MemoryRegionsMap>(
      base::NativeUnwinderAndroid::CreateMaps(),
      base::NativeUnwinderAndroid::CreateProcessMemory());
}

static_assert(std::is_same<stack_unwinder::CreateMemoryRegionsMapFunction,
                           decltype(&CreateMemoryRegionsMap)>::value,
              "CreateMemoryRegionsMapFunction typedef must match the declared "
              "function type");

std::unique_ptr<base::Unwinder> CreateNativeUnwinder(
    stack_unwinder::MemoryRegionsMap* memory_regions_map,
    uintptr_t exclude_module_with_base_address) {
  // The user is expected to only pass the subclass generated by
  // CreateMemoryRegionsMap().
  MemoryRegionsMap* concrete_memory_regions_map =
      static_cast<MemoryRegionsMap*>(memory_regions_map);
  return std::make_unique<base::NativeUnwinderAndroid>(
      concrete_memory_regions_map->maps(),
      concrete_memory_regions_map->memory(), exclude_module_with_base_address);
}

static_assert(std::is_same<stack_unwinder::CreateNativeUnwinderFunction,
                           decltype(&CreateNativeUnwinder)>::value,
              "CreateNativeUnwinderFunction typedef must match the declared "
              "function type");

static jlong
JNI_StackUnwinderModuleContentsImpl_GetCreateMemoryRegionsMapFunction(
    JNIEnv* env) {
  return reinterpret_cast<jlong>(&CreateMemoryRegionsMap);
}

static jlong
JNI_StackUnwinderModuleContentsImpl_GetCreateNativeUnwinderFunction(
    JNIEnv* env) {
  return reinterpret_cast<jlong>(&CreateNativeUnwinder);
}
